use core::ops::{Deref, DerefMut};

use utils::MutCell;

use crate::{
    c_src::{
        osMutexAcquire, osMutexAttr_t, osMutexDelete, osMutexId_t, osMutexNew, osMutexPrioInherit,
        osMutexRecursive, osMutexRelease, osMutexRobust, osRtxMutex_t,
    },
    osWaitForever,
};

use super::{DynamicMem, Error};

const CB_SIZE: usize = size_of::<osRtxMutex_t>();

unsafe impl Send for RawMutex {}
unsafe impl Sync for RawMutex {}

pub struct RawMutex {
    id: osMutexId_t,
    _cb_mem: DynamicMem,
}

impl Drop for RawMutex {
    fn drop(&mut self) {
        unsafe {
            osMutexDelete(self.id);
        }
    }
}

impl RawMutex {
    
    pub fn new2(name: &'static str) -> Result<Self, Error> {
        let cb_mem = DynamicMem::new2(CB_SIZE)?;
        let id = unsafe {
            let attr = osMutexAttr_t {
                name: name.as_ptr() as _,
                attr_bits: osMutexPrioInherit | osMutexRecursive | osMutexRobust,
                cb_mem: cb_mem.ptr as _,
                cb_size: CB_SIZE as _,
            };
            osMutexNew(&attr)
        };
        if id.is_null() {
            Err(Error::New)
        } else {
            Ok(Self {
                id,
                _cb_mem: cb_mem,
            })
        }
    }

    pub fn new(name: &'static str) -> Self {
        let cb_mem = DynamicMem::new(CB_SIZE);
        let id = unsafe {
            let attr = osMutexAttr_t {
                name: name.as_ptr() as _,
                attr_bits: osMutexPrioInherit | osMutexRecursive | osMutexRobust,
                cb_mem: cb_mem.ptr as _,
                cb_size: CB_SIZE as _,
            };
            osMutexNew(&attr)
        };
        if id.is_null() {
            panic!("mutex new fail");
        }
        Self {
            id,
            _cb_mem: cb_mem,
        }
    }

    pub fn free<T>(&self, f: impl Fn() -> T) -> T {
        self.lock();
        let n = f();
        self.unlock();
        n
    }

    pub fn lock(&self) {
        unsafe {
            osMutexAcquire(self.id, osWaitForever);
        }
    }

    pub fn unlock(&self) {
        unsafe {
            osMutexRelease(self.id);
        }
    }
}

pub struct MutexGuard<'a, T> {
    raw: &'a RawMutex,
    value: &'a mut T,
}

pub struct Mutex<T> {
    value: MutCell<T>,
    raw: RawMutex,
}

impl<'a, T> Drop for MutexGuard<'a, T> {
    fn drop(&mut self) {
        self.raw.unlock();
    }
}

impl<'a, T> Deref for MutexGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        self.value
    }
}

impl<'a, T> DerefMut for MutexGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.value
    }
}

impl<T> Mutex<T> {
    pub fn new(value: T) -> Self {
        Self {
            value: MutCell::const_new(value),
            raw: RawMutex::new("mutex"),
        }
    }

    pub fn lock<'a>(&'a self) -> MutexGuard<'a, T> {
        self.raw.lock();
        MutexGuard {
            raw: &self.raw,
            value: unsafe { self.value.as_mut() },
        }
    }
}
